import OSS from 'ali-oss';
import { ossClient, isOSSConfigured, ossConfig } from '../config/oss';
import { createError } from '../middleware/errorHandler';
import * as crypto from 'crypto';
import * as path from 'path';

// 文件上传接口
export interface UploadFileOptions {
  folder?: string;
  filename?: string;
  acl?: 'private' | 'public-read' | 'public-read-write';
  contentType?: string;
}

// 文件信息接口
export interface FileInfo {
  url: string;
  key: string;
  size: number;
  contentType: string;
  bucket: string;
  etag: string;
}

// 批量删除结果接口
export interface DeleteResult {
  deleted: string[];
  failed: { key: string; error: string }[];
}

// 预签名URL选项
export interface PresignedUrlOptions {
  expires?: number; // 过期时间（秒）
  method?: 'GET' | 'PUT' | 'POST' | 'DELETE';
  contentType?: string;
  process?: string; // 图片处理参数
}

export class OSSService {
  private client: OSS | null;

  constructor() {
    this.client = ossClient;
  }

  /**
   * 检查OSS是否可用
   */
  private checkOSSAvailability(): void {
    if (!isOSSConfigured() || !this.client) {
      throw createError('OSS服务未配置或不可用', 503);
    }
  }

  /**
   * 生成唯一的文件名
   */
  private generateFileName(originalName: string, folder?: string): string {
    const ext = path.extname(originalName);
    const timestamp = Date.now();
    const random = crypto.randomBytes(8).toString('hex');
    const filename = `${timestamp}_${random}${ext}`;
    
    return folder ? `${folder}/${filename}` : filename;
  }

  /**
   * 验证文件类型
   */
  private validateFileType(file: Express.Multer.File, allowedTypes?: string[]): void {
    if (allowedTypes && allowedTypes.length > 0) {
      const fileType = file.mimetype;
      if (!allowedTypes.includes(fileType)) {
        throw createError(`不支持的文件类型: ${fileType}`, 400);
      }
    }
  }

  /**
   * 验证文件大小
   */
  private validateFileSize(file: Express.Multer.File, maxSize?: number): void {
    if (maxSize && file.size > maxSize) {
      const maxSizeMB = Math.round(maxSize / 1024 / 1024);
      throw createError(`文件大小超过限制: ${maxSizeMB}MB`, 400);
    }
  }

  /**
   * 上传单个文件
   */
  async uploadFile(
    file: Express.Multer.File, 
    options: UploadFileOptions = {}
  ): Promise<FileInfo> {
    this.checkOSSAvailability();

    try {
      const {
        folder = 'uploads',
        filename,
        acl = 'public-read',
        contentType = file.mimetype
      } = options;

      const key = filename || this.generateFileName(file.originalname, folder);
      
      const result = await this.client!.put(key, file.buffer, {
        headers: {
          'Content-Type': contentType,
          'x-oss-object-acl': acl
        }
      });

      return {
        url: result.url,
        key: key,
        size: file.size,
        contentType: contentType,
        bucket: ossConfig.bucket,
        etag: (result.res.headers as any).etag || ''
      };
    } catch (error: any) {
      console.error('文件上传失败:', error);
      throw createError(`文件上传失败: ${error.message}`, 500);
    }
  }

  /**
   * 批量上传文件
   */
  async uploadFiles(
    files: Express.Multer.File[],
    options: UploadFileOptions = {}
  ): Promise<FileInfo[]> {
    const uploadPromises = files.map(file => this.uploadFile(file, options));
    return Promise.all(uploadPromises);
  }

  /**
   * 上传文件流或Buffer
   */
  async uploadBuffer(
    buffer: Buffer,
    key: string,
    options: UploadFileOptions = {}
  ): Promise<FileInfo> {
    this.checkOSSAvailability();

    try {
      const {
        acl = 'public-read',
        contentType = 'application/octet-stream'
      } = options;

      const result = await this.client!.put(key, buffer, {
        headers: {
          'Content-Type': contentType,
          'x-oss-object-acl': acl
        }
      });

      return {
        url: result.url,
        key: key,
        size: buffer.length,
        contentType: contentType,
        bucket: ossConfig.bucket,
        etag: (result.res.headers as any).etag || ''
      };
    } catch (error: any) {
      console.error('Buffer上传失败:', error);
      throw createError(`Buffer上传失败: ${error.message}`, 500);
    }
  }

  /**
   * 下载文件
   */
  async downloadFile(key: string): Promise<Buffer> {
    this.checkOSSAvailability();

    try {
      const result = await this.client!.get(key);
      return result.content as Buffer;
    } catch (error: any) {
      console.error('文件下载失败:', error);
      if (error.code === 'NoSuchKey') {
        throw createError('文件不存在', 404);
      }
      throw createError(`文件下载失败: ${error.message}`, 500);
    }
  }

  /**
   * 获取文件信息
   */
  async getFileInfo(key: string): Promise<any> {
    this.checkOSSAvailability();

    try {
      const result = await this.client!.head(key);
      const headers = result.res.headers as any;
      return {
        key,
        size: parseInt(headers['content-length'] || '0'),
        contentType: headers['content-type'] || '',
        lastModified: new Date(headers['last-modified'] || Date.now()),
        etag: headers.etag || ''
      };
    } catch (error: any) {
      console.error('获取文件信息失败:', error);
      if (error.code === 'NoSuchKey') {
        throw createError('文件不存在', 404);
      }
      throw createError(`获取文件信息失败: ${error.message}`, 500);
    }
  }

  /**
   * 删除单个文件
   */
  async deleteFile(key: string): Promise<void> {
    this.checkOSSAvailability();

    try {
      await this.client!.delete(key);
    } catch (error: any) {
      console.error('文件删除失败:', error);
      if (error.code === 'NoSuchKey') {
        throw createError('文件不存在', 404);
      }
      throw createError(`文件删除失败: ${error.message}`, 500);
    }
  }

  /**
   * 批量删除文件
   */
  async deleteFiles(keys: string[]): Promise<DeleteResult> {
    this.checkOSSAvailability();

    const result: DeleteResult = {
      deleted: [],
      failed: []
    };

    for (const key of keys) {
      try {
        await this.client!.delete(key);
        result.deleted.push(key);
      } catch (error: any) {
        result.failed.push({
          key,
          error: error.message
        });
      }
    }

    return result;
  }

  /**
   * 列出文件
   */
  async listFiles(
    prefix?: string,
    maxKeys: number = 100,
    continuationToken?: string
  ): Promise<{
    files: Array<{
      key: string;
      size: number;
      lastModified: Date;
      etag: string;
    }>;
    nextContinuationToken?: string;
    isTruncated: boolean;
  }> {
    this.checkOSSAvailability();

    try {
      const options: any = {
        'max-keys': maxKeys
      };

      if (prefix) {
        options.prefix = prefix;
      }

      if (continuationToken) {
        options.marker = continuationToken;
      }

      const result = await this.client!.list(options, {});

      const files = result.objects?.map(obj => ({
        key: obj.name,
        size: obj.size,
        lastModified: new Date(obj.lastModified),
        etag: obj.etag
      })) || [];

      return {
        files,
        nextContinuationToken: result.nextMarker,
        isTruncated: result.isTruncated
      };
    } catch (error: any) {
      console.error('列出文件失败:', error);
      throw createError(`列出文件失败: ${error.message}`, 500);
    }
  }

  /**
   * 生成预签名URL
   */
  async generatePresignedUrl(
    key: string,
    options: PresignedUrlOptions = {}
  ): Promise<string> {
    this.checkOSSAvailability();

    try {
      const {
        expires = 3600, // 默认1小时
        method = 'GET',
        contentType,
        process
      } = options;

      const urlOptions: any = {
        expires,
        method
      };

      if (contentType) {
        urlOptions['content-type'] = contentType;
      }

      if (process) {
        urlOptions.process = process;
      }

      const url = await this.client!.signatureUrl(key, urlOptions);
      return url;
    } catch (error: any) {
      console.error('生成预签名URL失败:', error);
      throw createError(`生成预签名URL失败: ${error.message}`, 500);
    }
  }

  /**
   * 复制文件
   */
  async copyFile(sourceKey: string, targetKey: string): Promise<void> {
    this.checkOSSAvailability();

    try {
      await this.client!.copy(targetKey, sourceKey);
    } catch (error: any) {
      console.error('文件复制失败:', error);
      if (error.code === 'NoSuchKey') {
        throw createError('源文件不存在', 404);
      }
      throw createError(`文件复制失败: ${error.message}`, 500);
    }
  }

  /**
   * 检查文件是否存在
   */
  async fileExists(key: string): Promise<boolean> {
    this.checkOSSAvailability();

    try {
      await this.client!.head(key);
      return true;
    } catch (error: any) {
      if (error.code === 'NoSuchKey') {
        return false;
      }
      throw createError(`检查文件是否存在失败: ${error.message}`, 500);
    }
  }
}